package com.fjbdxk.beidou.bd;

import java.util.LinkedList;
import java.util.Date;
import java.util.List;

import static com.fjbdxk.beidou.bd.PacketHeader.PACK_HEADER_SIZE;

/**
 * 用于存放一次待发送设备（风机）的数据集合
 * 比如发送10台，则将这10台数据
 * 构建特定格式的设备数据通信报文的工具，通过管理设备数据和选项，确保所生成的报文符合预定义的结构和限制。
 */
//管理要发送的设备数据，并根据不同的报文类型构建符合协议格式的字节数组
public class Devices4Send {
    final static int OPT_NUM_MAX = 2;//可选TP最大结构数量
    final static int OPT_NUM_TP2_MAX = 44;//TP2最大结构数量
    final static int DATA_NUM_MAX = 10;//最大数据结构数量
    final static int BD_PACK_L2 = 229;
    final Date dtBase;//基础时间，记录时间戳
    final PacketHeader.TP type;//报文类型
    final List<DeviceValues4Send> list = new LinkedList<>();
    //接受一个日期和一个整数（报文类型的代码），初始化类的成员变量 dtBase 和 type
    public Devices4Send(Date dtBase, int type) {
        this.dtBase = dtBase;
        this.type = PacketHeader.TP.getByCode(type);
    }

    public boolean addDeviceValues(DeviceValues4Send deviceValues4Send) {
        return list.add(deviceValues4Send);
    }
    //格式化和打包数据报文
    public byte[] formatPack(List<PacketOption> listOpt) throws Exception {
        int optNum = 0;
        int dataNum = list.size();
        if (dataNum > DATA_NUM_MAX) {
            throw new Exception("非纯报警报文，仅允许 10 个数据结构，实际提供了 " + dataNum + " 个。");
        }
        if (listOpt != null) {
            int size = listOpt.size();
            if ((type == PacketHeader.TP.TP_0 || type == PacketHeader.TP.TP_1)
                && size > OPT_NUM_MAX) {
                throw new Exception("非纯报警报文, 仅允许 2 个可选结构，实际提供了 " + size + " 个。");
            } else if (type == PacketHeader.TP.TP_2 && size > OPT_NUM_TP2_MAX) {
                throw new Exception("非纯报警报文, 仅允许 44 个可选结构，实际提供了 " + size + " 个。");
            }

            optNum = size;
        }


        byte[] pack = new byte[BD_PACK_L2];
        int offset = PACK_HEADER_SIZE;  // 报头长度为8
        if (type == PacketHeader.TP.TP_2) {// 纯报警报文
            dataNum = (0x0F & optNum);
            optNum = ((0x30 & optNum) >> 4);
            byte[] buf = formatOptPack(listOpt);
            assert buf.length <= pack.length - offset;
            System.arraycopy(buf, 0, pack, offset, buf.length);
            offset += buf.length;
        } else if (type == PacketHeader.TP.TP_0
                || type == PacketHeader.TP.TP_1) {// 数据报文
            byte[] buf = formatDataPack();
            System.arraycopy(buf, 0, pack, offset, buf.length);
            offset += buf.length;
            if (optNum > 0) {
                buf = formatOptPack(listOpt);
                System.arraycopy(buf, 0, pack, offset, buf.length);
                offset += buf.length;
            }
        }
        assert offset <= pack.length - 1;

        // 最后形成报文头
        int len = offset - 1;
        pack[0] = (byte)0xBD;
        pack[1] = (byte)len;
        pack[2] = PacketHeader.formatVSQ(type, optNum, dataNum);
        byte[] buf_dt = PacketHeader.formatDatetimeBytes(dtBase);
        System.arraycopy(buf_dt, 0, pack, 3, buf_dt.length);
        pack[offset] = (byte)0xCC;

        // 形成最终报文
        byte[] pack4send = new byte[1 + offset];
        System.arraycopy(pack, 0, pack4send, 0, 1 + offset);
        return pack4send;
    }
    //将多个 PacketOption 对象转换为字节数组，处理选项数据
    private byte[] formatOptPack(List<PacketOption> listOpt) {
        assert listOpt.size() > 0 && listOpt.size() <= OPT_NUM_TP2_MAX;
        byte[] buf = new byte[PacketOption.LEN * listOpt.size()];
        int idx = 0;
        for (PacketOption opt : listOpt) {
            byte[] buf_opt = opt.getBytes(dtBase);
            for (byte b : buf_opt) {
                buf[idx] = b;
                idx++;
            }
        }
        return buf;
    }
    //将多个 DeviceValues4Send 对象转换为字节数组，处理设备数据
    private byte[] formatDataPack() {
        int offset = 0;
        byte[] buf = new byte[256];

        for (DeviceValues4Send deviceValues : list) {
            byte[] buf_data = deviceValues.getBytes();
            System.arraycopy(buf_data, 0, buf, offset, buf_data.length);
            offset += buf_data.length;
        }
        byte[] pack = new byte[offset];
        System.arraycopy(buf, 0, pack, 0, offset);
        return pack;
    }
}
